/*************************************************************************
                         dicoOfDico  -  interface
                             -------------------
    debut                : 10 oct. 2009
    copyright            : (C) 2009 par Binome 3310
*************************************************************************/

//------ Interface de la classe <dicoOfDico> (fichier dicoOfDico.h) ------

#ifndef dicoOfDico_H_
#define dicoOfDico_H_

//--------------------------------------------------- Interfaces utilisées
#include <map>
#include <set>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;

//------------------------------------------------------------- Constantes

//------------------------------------------------------------------ Types
typedef enum codeRetour {OK, ERROR_KEY1, ERROR_KEY2, ERROR_DATA} codeRetour;
// OK : L'opération s'est déroulé correctement
// ERROR_KEY1 : La clef1 n'est pas dans le dictionnaire
// ERROR_KEY2 : La clef2 n'est pas dans le dictionnaire
// ERROR_DATA : La donnée n'est pas dans le dictionnaire

template < class Type3, class Type2, class Type1 >

class dicoOfDico
{

	typedef multiset< Type1 > liste;
	typedef map<Type2, liste> dico;
	typedef map<Type3, dico> dicoOfDicos;

//----------------------------------------------------------------- PUBLIC

	public:

//----------------------------------------------------- Methodes publiques

		void insert(Type3 key1, Type2 key2, Type1 data)
		// Mode d'emploi :	Insére un triplet dans le dictionnaire
		//
		// Contrat :
		//
		{
			dico firstDico = dictionnary[key1];
			liste dataList = firstDico[key2];

			dataList.insert (data);
			firstDico[key2] = dataList;
			dictionnary[key1] = firstDico;
		}


		void display()
		// Mode d'emploi : 	Affiche tous les triplets du dictionnaire
		//					Si le dictionnaire est vide
		//						-> Le message "Dictionnaire vide" est affiché
		//					Sinon
		//						-> Les triplets du dictionnaires sont affichés
		//
		// Contrat :
		//
		{
			if (dictionnary.empty() == 0)
			{
				for(typename dicoOfDicos::const_iterator it = dictionnary.begin(); it != dictionnary.end(); ++it)
				{
					cout << it->first << " -> ";

					for(typename dico::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2)
					{
						cout << it2->first << " -> ";
						copy(it2->second.begin(), it2->second.end(), ostream_iterator<Type1>(cout, " "));
					}

					cout << endl;
				}
			}
			else
			{
				cout << "Dictionnaire vide" << endl;
			}
		}

		void display(Type3 key)
		// Mode d'emploi : 	Affiche tous les couples du dictionnaire correspondant à une clef1 donnée
		//					Si la clef n'est pas dans le dictionnaire
		//						-> Le message "La clef n'est pas dans le dictionnaire" est affiché
		//					Sinon
		//						-> Les couples correspondant à la clef sont affichés
		//
		// Contrat :
		//
		{
			if (dictionnary.find(key) != dictionnary.end())
			{
				typename dicoOfDicos::const_iterator it = dictionnary.find(key);

					for(typename dico::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2)
					{
						cout << it2->first << " -> ";
						copy(it2->second.begin(), it2->second.end(), ostream_iterator<Type1>(cout, " "));
					}

				cout << endl;
			}
			else
			{
				cout << "La clef n'est pas dans le dictionnaire" << endl;
			}
		}

		void display(Type3 key1, Type2 key2)
		// Mode d'emploi : 	Affiche toutes les données du dictionnaire correspondant à une clef1 et une clef2 données
		//					Si la clef1 n'est pas dans le dictionnaire
		//						-> Le message "La clef1 n'est pas dans le dictionnaire" est affiché
		//					Sinon
		//						Si la clef2 n'est pas dans le dictionnaire
		//							-> Le message "La clef2 n'est pas dans le dictionnaire" est affiché
		//						Sinon
		//							-> Toutes les données correspondant aux clefs sont affichées
		//
		// Contrat :
		//
		{
			if (dictionnary.find(key1) != dictionnary.end())
			{
				if (dictionnary.find(key1)->second.find(key2) != dictionnary.find(key1)->second.end())
				{
					typename dico::const_iterator it2 = dictionnary.find(key1)->second.find(key2);

					copy(it2->second.begin(), it2->second.end(), ostream_iterator<Type1>(cout, " "));

					cout << endl;
				}
				else
				{
					cout << "La clef 2 n'est pas dans le dictionnaire" << endl;
				}
			}
			else
			{
				cout << "La clef 1 n'est pas dans le dictionnaire" << endl;
			}

		}

		codeRetour getContent(Type3 key, dico &newDico)
		// Mode d'emploi : 	Récupère tous les couples du dictionnaire correspondant à une clef donnée
		//					Si la clef n'est pas dans le dictionnaire
		//						-> Aucun couple n'est récupéré et le code ERROR_KEY1 est retourné
		//					Sinon
		//						-> Tous les couples correspondant à la clef sont récupéré et le code OK est retourné
		//
		// Contrat : Les couples sont récupérés dans une map < Type2, multiset < Type1 > >
		//
		{
			if (dictionnary.find(key) != dictionnary.end())
			{
				newDico = dictionnary[key];
				return OK;
			}

			return ERROR_KEY1;
		}

		codeRetour getContent(Type3 key1, Type2 key2, liste &newList)
		// Mode d'emploi : 	Récupère toutes les données du dictionnaire correspondant à une clef1 et à une clef2 données
		//					Si la clef1 n'est pas dans le dictionnaire
		//						-> Aucune donnée n'est récupérée et le code ERROR_KEY1 est retourné
		//					Sinon
		//						Si la clef2 n'est pas dans le dictionnaire
		//							-> Aucune donnée n'est récupérée et le code ERROR_KEY2 est retourné
		//						Sinon
		//						-> Toutes les données correspondant aux clefs sont récupérées et le code OK est retourné
		//
		// Contrat : Les données sont récupérés dans une map< Type3, ma < Type2, multiset< Type1 > > >
		//
		{
			if (dictionnary.find(key1) != dictionnary.end())
			{
				dico newDico = dictionnary[key1];

				if (newDico.find(key2) != newDico.end())
				{
					newList = newDico[key2];
					return OK;
				}
				else
				{
					return ERROR_KEY2;
				}
			}

			return ERROR_KEY1;
		}

		codeRetour deleteContent(Type3 key)
		// Mode d'emploi : 	Supprime la clef et tous les couples du dictionnaire correspondant à cette clef donnée
		//					Si la clef n'est pas dans le dictionnaire
		//						-> Rien n'est supprimé et le code ERROR_KEY1 est retourné
		//					Sinon
		//						-> La clef et tous les couples lui correspondant sont supprimés et le code OK est retourné
		//
		{
			if (dictionnary.find(key) != dictionnary.end())
			{
				dictionnary.erase(key);
				return OK;
			}

			return ERROR_KEY1;
		}

		codeRetour deleteContent(Type3 key1, Type2 key2)
		// Mode d'emploi : 	Supprime la clef2 et toutes les données du dictionnaire correspondant à une clef1 donnée
		//					Si la clef1 n'est pas dans le dictionnaire
		//						-> Rien n'est supprimé et le code ERROR_KEY1 est retourné
		//					Sinon
		//						Si la clef2 n'est pas dans le dictionnaire
		//							-> Rien n'est supprimé et le code ERROR_KEY2 est retourné
		//						Sinon
		//							-> La clef2 et toutes les données lui correspondant sont supprimés et le code OK est retourné
		//
		{
			if (dictionnary.find(key1) != dictionnary.end())
			{
				if (dictionnary.find(key1)->second.find(key2) != dictionnary.find(key1)->second.end())
				{
					dictionnary.find(key1)->second.erase(key2);

					if (dictionnary.count(key1) == 0)
					{
						dictionnary.erase(key1);
					}
					return OK;
				}

				return ERROR_KEY2;
			}

			return ERROR_KEY1;
		}

		codeRetour deleteContent(Type3 key1, Type2 key2, Type1 data)
		// Mode d'emploi : 	Supprime la donnée du dictionnaire correspondant à une clef1 et une clef2 données
		//					Si la clef1 n'est pas dans le dictionnaire
		//						-> Rien n'est supprimé et le code ERROR_kEY1 est retourné
		//					Sinon
		//						Si la clef2 n'est pas dans le dictionnaire
		//							-> Rien n'est supprimé et le code ERROR_KEY2 est retourné
		//						Sinon
		//							Si la donnée n'est pas dans le dictionnaire
		//								-> Rien n'est supprimé et le code ERROR_DATA est retourné
		//							Sinon
		//								-> La donnée est supprimé et le code OK est retourné
		//
		{
			if (dictionnary.find(key1) != dictionnary.end())
			{
				if (dictionnary.find(key1)->second.find(key2) != dictionnary.find(key1)->second.end())
				{
					if (dictionnary.find(key1)->second.find(key2)->second.find(data) != dictionnary.find(key1)->second.find(key2)->second.end())
					{
						dictionnary.find(key1)->second.find(key2)->second.erase(data);

						if (dictionnary.find(key1)->second.count(key2) == 0)
						{
							dictionnary.find(key1)->second.erase(key2);
						}

						if (dictionnary.count(key1) == 0)
						{
							dictionnary.erase(key1);
						}

						return OK;
					}
					return ERROR_DATA;
				}

				return ERROR_KEY2;
			}

			return ERROR_KEY1;
		}

//-------------------------------------------------- Surcharge d'opérateur

//-------------------------------------------- Constructeurs - destructeur

		dicoOfDico() {};
		// Mode d'emploi : Crée un dictionnaire vide

		~dicoOfDico() {};
		// Mode d'emploi : Détruit le dictionnaire

//------------------------------------------------------------------ PRIVE

	protected:

//----------------------------------------------------- Méthodes protégées

//----------------------------------------------------- Attributs protégés

		dicoOfDicos dictionnary;
		// map< TYPE3, map< TYPE2, multiset< TYPE1 > > >
};

//------- Autres définitions dépendantes de <dicoOfDico> ------------------

#endif /* DICOOFDICO_H_ */
